package jwt

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt"

	"hyper/supports/configs"
	"hyper/supports/helpers"
	"hyper/supports/redis"
)

const EXPIRES = time.Hour * 24

// 创建token
func CreateToken(attributes interface{}, flag string) (string, error) {
	// 创建 token
	/**
	 * |-------------------创建 token----------------|
	 * |         iss(Issuer)   : 发行人              |
	 * |         exp(ExpiresAt)：到期时间             |
	 * |         sub(Subject)  ：主题                |
	 * |         aud(Audience) ：用户                |
	 * |         nbf(NotBefore)：在此之前不可用        |
	 * |         iat(IssuedAt) ：发布时间             |
	 * |         jti(Id)       ：JWT ID用于标识该JWT  |
	 * |--------------------------------------------|
	 */
	token := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{
		"iss":  configs.Get("app.name"),                           // 发行人
		"sub":  time.Now().Unix(),                                 // 主题
		"aud":  configs.Get("app.name"),                           // 用户
		"iat":  time.Now().Unix(),                                 // 发布时间
		"exp":  time.Now().Add(time.Duration(EXPIRES * 2)).Unix(), // 到期时间
		"data": attributes,
	})

	// 使用签名方法和密钥签署 token
	tokenString, err := token.SignedString([]byte(configs.Get("app.key")))

	if !helpers.Empty(flag) {
		// todo:
		SingleSignOn(tokenString, flag)
	}

	return tokenString, err
}

// 解析token
func ParseToken(tokenString string) (jwt.MapClaims, error) {

	// 分解规定，我们使用.进行分隔，所以我们通过.进行分隔成三个字符串的数组
	jwtParts := strings.Split(tokenString, ".")
	if len(jwtParts) != 3 {
		return nil, errors.New("invalid token")
	}

	// 解析 token
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {

		// 确保 token 使用了预期的签名方法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}

		return []byte(configs.Get("app.key")), nil
	})

	if err != nil {
		return nil, errors.New("invalid token")
	}

	// 验证 token
	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return claims, nil
		// fmt.Println("Token is valid.")
		// fmt.Println("User ID:", claims["sub"])
		// fmt.Println("User Name:", claims["name"])
		// fmt.Println("Issued At:", time.Unix(int64(claims["iat"].(float64)), 0))
		// fmt.Println("Expires At:", time.Unix(int64(claims["exp"].(float64)), 0))
	} else {
		return nil, errors.New("invalid token")
	}
}

// token验证是否有效
func VerifyToken(tokenString string) bool {

	// 验证token是否合法
	payload, err := ParseToken(tokenString)
	if err != nil {
		return false
	}

	// 签发时间大于当前服务器时间验证失败
	if payload["iat"] == "" && time.Now().Before(time.Unix(int64(payload["iat"].(float64)), 0)) {
		return false
	}

	// 当前服务器时间大于过期时间验证失败
	if payload["exp"] == "" && time.Now().After(time.Unix(int64(payload["exp"].(float64)), 0)) {
		return false
	}

	return true
}

// 将token加入黑名单 用户退出
func AddTokenToBlacklist(tokenString string) bool {
	str := helpers.Data2Md5(tokenString)
	// todo:
	redis.Redis.Set(str, tokenString, time.Duration(EXPIRES*30))
	return true
}

// 判断token是否在黑名单中
func HashTokenInBlacklist(tokenString string) bool {
	str := helpers.Data2Md5(tokenString)
	// todo:
	if redis.Redis.Get(str) != "" {
		return true
	} else {
		return false
	}
}

// 用户单点登录，后者将前者给挤下线
func SingleSignOn(tokenString string, flag string) bool {
	// todo:
	str := redis.Redis.Get(flag)

	if str != "" {
		AddTokenToBlacklist(str)
	}
	redis.Redis.Set(flag, tokenString, time.Duration(EXPIRES*30))
	return true
}

// 获取 Authorization 头信息
func GetTokenFromHeader(c *gin.Context) (string, error) {

	authHeader := c.GetHeader("Authorization")
	if authHeader == "" {
		return "", errors.New("no authorization header")
	}

	// 按空格分割
	parts := strings.SplitN(authHeader, " ", 2)

	if len(parts) != 2 || strings.TrimSpace(parts[0]) != "Bearer" {
		return "", errors.New("invalid token")
	}

	return strings.TrimSpace(parts[1]), nil
}
